﻿using System;

namespace Creventive.Wpf.ShellFactory.Services
{
    /// <summary>
    ///   Provides a container for services.
    /// </summary>
    public interface IServiceContainer : IServiceProvider
    {
        /// <summary>
        ///   Creates a child container. Any services registered in the child will be unavailable in its parent container unless they were registered as promoted.
        /// </summary>
        /// <returns></returns>
        IServiceContainer CreateChild();

        /// <summary>
        ///   Registers the object factory for the given key type, creating an object of the given instance type.
        /// </summary>
        /// <param name = "keyType">Type of the service to manage.</param>
        /// <param name = "instanceType">Type of the instance.</param>
        /// <param name = "instantiationMode">The instantiation mode.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterServiceFactory(Type keyType, Type instanceType, InstantiationMode instantiationMode, bool promote = false);

        /// <summary>
        ///   Registers the given object factory.
        /// </summary>
        /// <param name = "keyType">Type of the service to manage.</param>
        /// <param name = "objectFactory">The object factory.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterServiceFactory(Type keyType, IServiceFactory objectFactory, bool promote = false);

        /// <summary>
        ///   Gets the service factory for the given key type.
        /// </summary>
        /// <param name = "keyType">Type of the service to manage.</param>
        /// <returns></returns>
        IServiceFactory GetServiceFactory(Type keyType);

        /// <summary>
        ///   Unregisters the service.
        /// </summary>
        /// <param name = "keyType">Type of the service to manage.</param>
        /// <param name = "promoted">if set to <c>true</c>, the entire container ancestry will be searched until the first instance of the service is found, then it will be removed.</param>
        void UnregisterService(Type keyType, bool promoted);

        /// <summary>
        ///   Attempts to resolve the given type. Throws an InvalidOperationException if the service is missing.
        /// </summary>
        /// <returns></returns>
        object EnsureService(Type serviceType);

        /// <summary>
        ///   Determines whether the specified service has been registered.
        /// </summary>
        /// <param name = "keyType">Type of the service to manage.</param>
        /// <returns>
        ///   <c>true</c> if the specified service has been registered; otherwise, <c>false</c>.
        /// </returns>
        bool HasService(Type keyType);

        /// <summary>
        ///   Registers the instance.
        /// </summary>
        /// <typeparam name = "T">The type of the service to manage.</typeparam>
        /// <param name = "instance">The instance.</param>
        /// <param name = "lifetime">The lifetime.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterService<T>(T instance, ServiceLifetime lifetime, bool promote = false) where T : class;

        /// <summary>
        ///   Registers the object factory for the given key type, creating an object of the given instance type.
        /// </summary>
        /// <typeparam name = "Tkey">The type of the service to manage.</typeparam>
        /// <typeparam name = "Tinstance">The type of the instance.</typeparam>
        /// <param name = "instantiationMode">The instantiation mode.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterServiceFactory<Tkey, Tinstance>(InstantiationMode instantiationMode, bool promote = false)
            where Tkey : class
            where Tinstance : Tkey, new();

        /// <summary>
        ///   Registers the object factory for the type returned by the delegate.
        /// </summary>
        /// <typeparam name = "T">The type of the service to manage.</typeparam>
        /// <param name = "createInstance">The instance construction callback.</param>
        /// <param name = "instantiationMode">The instantiation mode.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterServiceFactory<T>(Func<T> createInstance, InstantiationMode instantiationMode, bool promote = false) where T : class;

        /// <summary>
        ///   Registers the given object factory.
        /// </summary>
        /// <typeparam name = "T">The type of the service to manage.</typeparam>
        /// <param name = "serviceFactory">The object factory.</param>
        /// <param name = "promote">if set to <c>true</c>, this service is registered at the lowest level ancestor container.</param>
        void RegisterServiceFactory<T>(IServiceFactory<T> serviceFactory, bool promote = false) where T : class;

        /// <summary>
        ///   Attempts to resolve the given type. Returns null if the service is missing.
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <returns></returns>
        T GetService<T>() where T : class;

        /// <summary>
        ///   Attempts to resolve the given type. Throws an InvalidOperationException if the service is missing.
        /// </summary>
        /// <returns></returns>
        T EnsureService<T>();

        /// <summary>
        ///   Gets the service factory.
        /// </summary>
        /// <typeparam name = "T">The type of the service to manage.</typeparam>
        /// <returns></returns>
        IServiceFactory<T> GetServiceFactory<T>() where T : class;

        /// <summary>
        ///   Determines whether the specified service has been registered.
        /// </summary>
        /// <typeparam name = "T">The type of the service to manage.</typeparam>
        /// <returns>
        ///   <c>true</c> if the specified service has been registered; otherwise, <c>false</c>.
        /// </returns>
        bool HasService<T>();
    }
}